﻿using Microsoft.Win32.SafeHandles;
using MinHook;
using SDK.Script.AIModuleSDK;
using SDK.Script.CoreUObjectSDK;
using SDK.Script.EngineSDK;
using SDK.Script.InputCoreSDK;
using SDK.Script.MovieSceneSDK;
using SDK.Script.PalSDK;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using UnrealSharp;

namespace PalWorldAntiCheat
{
    public unsafe static class Program
    {
        public static void AntiCheat(nint Class, nint Function, nint Parms)
        {
            var obj = new UEObject((nint)Class);
            if (obj.IsA(out PalPlayerCharacter playerChar) && playerChar.Controller.IsA(out PalPlayerController controller))
            {
                string funcName = obj.GetFuncName(obj.ClassAddr, obj.ClassAddr, Function);
                if (funcName == null)
                {
                    UEObject.OrigProcessEvent(Class, Function, Parms);
                    return;
                }
                if ("ReviveCharacter_ToServer".Equals(funcName))
                {
                    playerChar.K2_DestroyActor();
                    controller.K2_DestroyActor();
                    return;
                }
                if ("K2_OnMovementModeChanged".Equals(funcName) && ((*(long*)Parms) & 0x400) == 0x400 && !controller.IsRidingFlyPal())
                {
                    playerChar.K2_DestroyActor();
                    controller.K2_DestroyActor();
                    return;
                }     
            }
            else if(obj.IsA(out PalNetworkPlayerComponent netPlayer))
            {
                string funcName = obj.GetFuncName(obj.ClassAddr, obj.ClassAddr, Function);
                if (funcName == null)
                {
                    UEObject.OrigProcessEvent(Class, Function, Parms);
                    return;
                }
                if ("RequestAddItem_ToServer".Equals(funcName)) return;
                if ("RegisterRespawnLocation_ToServer".Equals(funcName)) return;
            }
            else if (obj.IsA(out PalPlayerState playerState))
            {
                string funcName = obj.GetFuncName(obj.ClassAddr, obj.ClassAddr, Function);
                if (funcName == null)
                {
                    UEObject.OrigProcessEvent(Class, Function, Parms);
                    return;
                }
                if ("SendDeath_ToServer".Equals(funcName)) return;
                if ("GrantExpForParty".Equals(funcName)) return;
            }

            //if ("RequestJoinGuildForPlayer_ToServer".Equals(funcName)) return;
            UEObject.OrigProcessEvent(Class, Function, Parms);
        }
        static World World;
        public static Int32 Main()
        {
            if (Process.GetCurrentProcess().ProcessName == typeof(Program).Namespace) NativeNetSharp.NativeNetSharp.InjectCore("PalServer-Win64-Test-Cmd", System.Reflection.Assembly.GetEntryAssembly().Location);
            else
            {
                try
                {
                    NativeNetSharp.NativeNetSharp.AllocConsole();
                    var standardOutput = new StreamWriter(new FileStream(new SafeFileHandle(NativeNetSharp.NativeNetSharp.GetStdHandle(-11), true), FileAccess.Write), Encoding.ASCII) { AutoFlush = true };
                    System.Console.SetOut(standardOutput);
                    System.Console.WriteLine("C# DLL loaded");
                    var ue = new UnrealEngine(new Memory(Process.GetCurrentProcess()));
                    System.Console.WriteLine("Updating addresses");
                    ue.UpdateAddresses();
                    UEObject.ProcessEventContinueHook += AntiCheat;
                    World = new World(UnrealEngine.Memory.ReadProcessMemory<nint>(UnrealEngine.GWorldPtr)); if (!World.IsA<World>()) return 1;
                    var OwningGameInstance = World.OwningGameInstance.As<PalGameInstance>(); if (!OwningGameInstance.IsA<PalGameInstance>()) return 2;
                    System.Console.WriteLine("Hooking Game Instance");
                    World.HookProcessEvent(); // global process event hook
                   /* var players = World.GameState.PlayerArray;
                    for(var i = 0; i < players.Num; i++)
                    {
                        var player = players[i];
                        System.Console.WriteLine("hooking player"); // sometimes this fails... also need to batch the hooks because of suspend thread
                        //player.HookProcessEvent();
                        //player.As<PalPlayerState>().PawnPrivate.HookProcessEvent();
                        //player.As<PalPlayerState>().InventoryData.HookProcessEvent();
                        //player.As<PalPlayerState>().TechnologyData.HookProcessEvent();
                        player.As<PalPlayerState>().PawnPrivate.Controller.HookProcessEvent();
                        //player.As<PalPlayerState>().PawnPrivate.Controller.As<PalPlayerController>().Transmitter.HookProcessEvent();
                        //player.As<PalPlayerState>().PawnPrivate.Controller.As<PalPlayerController>().Transmitter.Player.HookProcessEvent();
                        System.Console.WriteLine("hooked player");
                    }
                    // todo hook players joining*/
                    System.Console.WriteLine("Hooked Game Instance");
                    System.Console.WriteLine("AntiCheat Init!!");
                    System.Console.ReadLine();
                }
                catch (Exception e)
                {
                    System.Console.WriteLine("err" + " : " + e.Message);
                }
            }
            return 0;
        }
    }
}